/*
 *  Copyright (C) 2010 Pete Reisinger <p.reisinger@gmail.com>.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package paypalnvp.fields;

import java.util.HashMap;
import java.util.Map;
import paypalnvp.util.Validator;

/**
 * Billing Period Details Fields
 *
 * @author Pete Reisinger <p.reisinger@gmail.com>
 */
public final class BillingPeriodDetails implements RequestFields {

    /** Unit for billing during this subscription period */
    public enum BillingPeriod {

        DAY("Day"),
        WEEK("Week"),
        SEMI_MONTH("SemiMonth"),
        MONTH("Month"),
        YEAR("Year");

        private String value;

        private BillingPeriod(String value) {
            this.value = value;
        }

        /** returns value name for nvp request */
        String getValue() {
            return value;
        }
    }

    /** map that holds name value pair request values */
    private final Map<String, String> nvpRequest;

    /**
     *
     * @param period    Unit for billing during this subscription period.
     *                  For SemiMonth, billing is done on the 1st and 15th of
     *                  each month.
     *                  Note:
     *                  The combination of BillingPeriod and BillingFrequency
     *                  cannot exceed one year.
     * @param frequency Number of billing periods that make up one billing
     *                  cycle. The combination of billing frequency and billing
     *                  period must be less than or equal to one year. For
     *                  example, if the billing cycle is Month, the maximum
     *                  value for billing frequency is 12. Similarly, if the
     *                  billing cycle is Week, the maximum value for billing
     *                  frequency is 52.
     *                  Note:
     *                  If the billing period is SemiMonth., the billing frequency must be 1.
     * @param amount    Billing amount for each billing cycle during this
     *                  payment period. This amount does not include shipping
     *                  and tax amounts.
     *                  Note:
     *                  All amounts in the CreateRecurringPaymentsProfile
     *                  request must have the same currency.
     *                  Character length and limitations: Does not exceed
     *                  $10,000 USD in any currency. No currency symbol.
     *                  Regardless of currency, decimal separator is a period
     *                  (.). Equivalent to nine characters maximum for USD.
     * @param currency  Currency code.
     * @throws IllegalArgumentException
     */
    public BillingPeriodDetails(BillingPeriod period, int frequency,
            String amount, Currency currency) throws IllegalArgumentException {

        /* string represntation of the period */
        String billingPeriod = period.getValue();
        String billingFrequency = period.getValue();

        /* validation */
        if (frequency < 0) {
            throw new IllegalArgumentException("Billing frequency cannot be 0");
        }
        if (billingPeriod.equals("Year") && frequency > 365) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("Month") && frequency > 12) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("Week") && frequency > 52) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("Day") && frequency > 1) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("SemiMonth") && frequency != 1) {
            throw new IllegalArgumentException("When billing period is set to "
                    + "Semi month, then billing frequecny has to be 1");
        }

        if (!Validator.isValidAmount(amount)) {
            throw new IllegalArgumentException("Amount has to have exactly two "
					+ "decimal places seaprated by \".\" - example: \"50.00\"");
        }

        nvpRequest = new HashMap<String, String>();

        /* required values */
        nvpRequest.put("BILLINGPERIOD", billingPeriod);
        nvpRequest.put("BILLINGFREQUENCY", billingFrequency);
        nvpRequest.put("AMT", amount);
        nvpRequest.put("CURRENCYCODE", currency.toString());
    }

    /** 
     * The number of billing cycles for payment period.
     * 
     * @param billingCycles     For the regular payment period, if no value is 
     *                          specified or the value is 0, the regular 
     *                          payment period continues until the profile is 
     *                          cancelled or deactivated. 
     *                          For the regular payment period, if the value is 
     *                          greater than 0, the regular payment period will 
     *                          expire after the trial period is finished and 
     *                          continue at the billing frequency for 
     *                          TotalBillingCycles cycles.
     */
    public void setTotalBillingCycles(int billingCycles) {

        String totalBillingCycles = Integer.toString(billingCycles);
        nvpRequest.put("TOTALBILLINGCYCLES", totalBillingCycles);
    }

    /**
     * 
     * @param period    Unit for billing during this subscription period. 
     *                  For SemiMonth, billing is done on the 1st and 15th of 
     *                  each month. 
     *                  Note: 
     *                  The combination of BillingPeriod and BillingFrequency 
     *                  cannot exceed one year.
     * @param frequency Number of billing periods that make up one billing 
     *                  cycle; required if you specify an optional trial 
     *                  period. The combination of billing frequency and 
     *                  billing period must be less than or equal to one year. 
     *                  For example, if the billing cycle is Month, the maximum 
     *                  value for billing frequency is 12. Similarly, if the 
     *                  billing cycle is Week, the maximum value for billing 
     *                  frequency is 52. 
     *                  Note: 
     *                  If the billing period is SemiMonth., the billing 
     *                  frequency must be 1.
     * @param amount    Billing amount for each billing cycle during this 
     *                  payment period. This amount does not include shipping 
     *                  and tax amounts. 
     *                  Note: 
     *                  All amounts in the CreateRecurringPaymentsProfile 
     *                  request must have the same currency. Character length 
     *                  and limitations: Does not exceed $10,000 USD in any 
     *                  currency. No currency symbol. Regardless of currency, 
     *                  decimal separator is a period (.). Equivalent to nine 
     *                  characters maximum for USD.
     * @throws IllegalArgumentException
     */
    public void setTrialBilling(BillingPeriod period, int frequency,
            String amount) throws IllegalArgumentException {

        /* string representations of period and frequency */
        String billingPeriod = period.getValue();
        String billingFrequency = Integer.toString(frequency);

        /* validation */
        if (billingPeriod.equals("Year") && frequency > 365) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("Month") && frequency > 12) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("Week") && frequency > 52) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("Day") && frequency > 1) {
            throw new IllegalArgumentException("The combination of billing "
                    + "frequency (" + billingFrequency  + ") and billing "
                    + "period (" + billingPeriod + ") must be less than or "
                    + "equal to one year.");
        }
        if (billingPeriod.equals("SemiMonth") && frequency != 1) {
            throw new IllegalArgumentException("When billing period is set to "
                    + "Semi month, then billing frequecny has to be 1");
        }

        if (!Validator.isValidAmount(amount)) {
            throw new IllegalArgumentException("Amount has to have exactly two "
					+ "decimal places seaprated by \".\" - example: \"50.00\"");
        }

        nvpRequest.put("TRIALBILLINGPERIOD", billingPeriod);
        nvpRequest.put("TRIALBILLINGFREQUENCY", billingFrequency);
        nvpRequest.put("TRIALAMT", amount);
    }

    /**
     *
     * @param billingCycles The number of billing cycles for trial payment
     *                      period.
     */
    public void setTrialBillingCycles(int billingCycles) {

        String trialBillingCycles = Integer.toString(billingCycles);
        nvpRequest.put("TRIALTOTALBILLINGCYCLES", trialBillingCycles);
    }

    /**
     *
     * @param amount    Shipping amount for each billing cycle during this
     *                  payment period.
     *                  Note:
     *                  All amounts in the request must have the same currency.
     *                  Character length and limitations: Does not exceed
     *                  $10,000 USD in any currency. No currency symbol.
     *                  Regardless of currency, decimal separator is a period
     *                  (.). Equivalent to nine characters maximum for USD.
     * @throws IllegalArgumentException
     */
    public void setShippingAmount(String amount)
            throws IllegalArgumentException {

        if (!Validator.isValidAmount(amount)) {
            throw new IllegalArgumentException("Amount has to have exactly two "
					+ "decimal places seaprated by \".\" - example: \"50.00\"");
        }

        nvpRequest.put("SHIPPINGAMT", amount);
    }

    /**
     *
     * @param amount    Tax amount for each billing cycle during this payment
     *                  period.
     *                  Note:
     *                  All amounts in the request must have the same currency.
     *                  Character length and limitations: Does not exceed
     *                  $10,000 USD in any currency. No currency symbol.
     *                  Regardless of currency, decimal separator is a period
     *                  (.). Equivalent to nine characters maximum for USD.
     * @throws IllegalArgumentException
     */
    public void setTaxAmount(String amount) throws IllegalArgumentException {

        if (!Validator.isValidAmount(amount)) {
            throw new IllegalArgumentException("Amount has to have exactly two "
					+ "decimal places seaprated by \".\" - example: \"50.00\"");
        }

        nvpRequest.put("TAXAMT", amount);
    }

    public Map<String, String> getNVPRequest() {
        return new HashMap<String, String>(nvpRequest);
    }

    @Override
    public String toString() {

        return "instance of BillingPeriodDetails class with the values: "
                + "nvpRequest: " + nvpRequest.toString();
    }
}
